﻿#region Copyright
// 
// Copyright (C) 2008 VirtualStaticVoid <virtualstaticvoid@gmail.com>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#endregion

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace NAom.DAL
{
  internal class AttributeTypeExpressionVisitor: ExpressionVisitor
  {
    
    private static readonly MethodInfo __getPropertyExtensionMethod;
    private static readonly MethodInfo __getAttributeTypeMethod;

    static AttributeTypeExpressionVisitor()
    {
      __getPropertyExtensionMethod = typeof(AttributeTypeExtensions).GetMethod("GetProperty", BindingFlags.Public | BindingFlags.Static);
      __getAttributeTypeMethod = typeof(AttributeTypeExpressionVisitor).GetMethod("GetAttributeType", BindingFlags.NonPublic | BindingFlags.Static);
    }

    private static IAttributeType GetAttributeType(object propertyType)
    {
      return propertyType as IAttributeType;
    }

    private readonly Type _dynamicType;

    public AttributeTypeExpressionVisitor(Type dynamicType)
    {
      _dynamicType = dynamicType;  
    }

    public Expression<TDelegate> RewriteAttributeTypeExpression<TDelegate>(Expression<TDelegate> expression)
    {
      return (Expression<TDelegate>)Visit(expression);
    }

    protected override Expression VisitMethodCall(MethodCallExpression m)
    {

      if (m.Method.GetGenericMethodDefinition() == __getPropertyExtensionMethod)
      {

        // rewrite call to extension method GetProperty(this TEntity, IAttributeType) 
        //  as a property call to the actual property, so that LINQ 2 SQL works as expected!

        //  arg 0 contains the instance
        //  arg 1 contains the entity attribute type
        Expression arg0 = m.Arguments[0];
        Expression arg1 = m.Arguments[1];

        LambdaExpression eval = Expression.Lambda(Expression.Call(__getAttributeTypeMethod, arg1));
        IAttributeType entityAttributeType = (IAttributeType)eval.Compile().DynamicInvoke();

        return Expression.MakeMemberAccess
          (
            Expression.TypeAs(arg0, _dynamicType),
            _dynamicType.GetProperty(entityAttributeType.Name)
          );

      }
      
      return base.VisitMethodCall(m);
    }

  }
}
